package com.espirit.eap.manager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import com.espirit.eap.pagelayout.Image;
import com.espirit.eap.pagelayout.Layout;
import com.espirit.eap.pagelayout.Page;
import com.espirit.eap.pagelayout.PageService;
import com.espirit.eap.pagelayout.PageUpgradeService;
import com.espirit.eap.pagelayout.SectionElement;
import com.googlecode.cswish.Config;
import com.googlecode.cswish.annotation.Rest;
import com.googlecode.cswish.model.PageInfo;
import com.googlecode.cswish.model.Reference;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.struts.spring.GenericService;

@Service
public class CommonTemplateService {

	private final static Logger logger = Logger.getLogger(PageUpgradeService.class);

	private final static String PKG_PREFIX = CommonTemplateService.class.getPackage().getName().substring(4);

	private final static String CLASS_NAME = "commonTemplate";
	
	private final static String PATH_PREFIX = "/static/decorators/";

	@Resource
	private Config config;

	@Resource
	private GenericService genericService;

	@Resource
	private PageService pageService;

	public String importTemplate(EapImport model) {
		if (model == null || model.getFile() == null) {
			return null;
		}

		File file = model.getFile();
		String fileType = model.getFileFileName();
		boolean isZip = fileType.endsWith("zip");
		Page template = null;
		Layout layout = null;
		String pkgName = null;
		String baseName = null;
		String decsPath = null;
		String basePath = config.getRealWebRootPath()+File.separatorChar+"static/decorators/";
		//unzip 
		if (isZip) {
			Date now = new Date();
			ZipFile zipFile = null;
			InputStream input = null;
			OutputStream output = null;
			File entryFile = null;
			template = new Page();
			template.setFreeAccess(true);
			template.setCommonTemplate(true);
			template.setTemplate(true);
			template.setModifyOn(now);
			List<SectionElement> resources = new ArrayList<>();
			SectionElement reference = new SectionElement();
			try {
				zipFile = new ZipFile(file);
				String entryName = null;
				boolean isFirst = true;
				Enumeration<? extends ZipEntry> emu = zipFile.entries();
				layout = new Layout();
				layout.setCreatedOn(now);
				layout.setModifiedOn(now);
				String dirName = null;
				while (emu.hasMoreElements()) {
					ZipEntry entry = emu.nextElement();
					entryName = entry.getName();
					if (entry.isDirectory()) {
						if (isFirst) {
							dirName = entryName.substring(0, entryName.indexOf('/'));
							baseName = dirName.toLowerCase();
							pkgName = PKG_PREFIX + "." + CLASS_NAME;
							layout.setName(baseName);
							template.setDescription(baseName);
							decsPath = "/" + pkgName.replace('.', '/') + "." + baseName + ".dec";
							template.setPath(decsPath);
							Page param = new Page();
							param.setCommonTemplate(true);
							param.setPath(decsPath);
							Page existed = genericService.searchAll(param).getData(0);
							if (existed != null) {
								List<Page> pages = new ArrayList<>();
								pages.add(existed);
								pageService.deleteList(pages);
								
							} 
							isFirst = false;
						}
						continue;
					}
					input = zipFile.getInputStream(entry);
					byte[] fileData = IOUtils.toByteArray(input);
					
					if (entryName.endsWith(".html")) {
						if (layout.safeChildren().indexOf(reference) == -1) {
							reference.setTagName("template");
							reference.setName(baseName);
							reference.addExProperty("url", PATH_PREFIX + entryName );
							layout.safeChildren().add(reference);
							fileData = modifyTemplateResourcePath(PATH_PREFIX + dirName, reference, fileData);
						} else {
							logger.error("Found 2 html pages, skip " + entryName + "!");
						}
					}else if(entryName.endsWith(".css")){
						SectionElement css = new SectionElement();
						css.setTagName("css");
						css.addExProperty("src", PATH_PREFIX + entryName );
						css.addExProperty("async", "false");
						resources.add(css);
					}else if(entryName.endsWith(".js")){
						SectionElement script = new SectionElement();
						script.setTagName("script");
						script.addExProperty("src", PATH_PREFIX+ entryName );
						script.addExProperty("async", "false");
						resources.add(script);
					}else if(entryName.endsWith(baseName.toLowerCase()+".jpg")){
						template.setPreviewFileName(PATH_PREFIX + entryName);
					}
					String filePath = basePath + entryName;
					entryFile = new File(filePath);
					if (!entryFile.getParentFile().exists()) {
						entryFile.getParentFile().mkdirs();
					}
					output = new FileOutputStream(entryFile);

					IOUtils.write(fileData, output);
				}
				
				reference.safeChildren().addAll(resources);
				for (SectionElement element : resources) {
					element.setParent(reference);
				}
			} catch (Exception ex) {
				logger.error("Fail to import template file", ex);
			} finally {
				if (zipFile != null) {
					try {
						zipFile.close();
					} catch (Exception ex) {
						logger.error("Fail to close zip file", ex);
					}
				}
				IOUtils.closeQuietly(input);
				IOUtils.closeQuietly(output);
			}
		}

		layout = genericService.merge(layout);
		template.setLayout(layout);
		Page saved = genericService.merge(template);
		return saved.getPath();
	}

	private byte[] modifyTemplateResourcePath(String pathPrefix, SectionElement reference, byte[] fileData) {
		Document document = Jsoup.parse(new String (fileData));
		Elements htmlTags = document.getElementsByTag("html");
		if (htmlTags.size() > 0) {
			Element htmlTag = htmlTags.get(0);
			reference.addExProperty("templateContainTagInfo", "true");
			htmlTag.attr("uiType", "template");
			htmlTag.attr("name", "_gen_" + reference.getName() + "_0");
			htmlTag.attr("url", reference.getExProperty("url"));
		}
		
		Elements elements = document.getElementsByTag("link");
		Element body = document.body();
		Element firstElement =body.child(0);
		if (elements!=null && !elements.isEmpty()) {
			Iterator<Element> iterator = elements.iterator();
			while (iterator.hasNext()) {
				Element element = iterator.next();;
				firstElement.before("[var]");
				element.remove();
			}
		}
		elements = document.getElementsByTag("script");
		if (elements!=null && !elements.isEmpty()) {
			Iterator<Element> iterator = elements.iterator();
			while (iterator.hasNext()) {
				Element element = iterator.next();;
				firstElement.before("[var]");
				element.remove();
			}
		}
		elements = document.getElementsByTag("img");
		if (elements!=null && !elements.isEmpty()) {
			Iterator<Element> iterator = elements.iterator();
			while (iterator.hasNext()) {
				Element element = iterator.next();;
				String url = element.attr("src");
				if (!StringUtils.isEmpty(url)) {
					 element.attr("src","${base}"+pathPrefix + "/" + url);
				}
			}
		}
		return document.toString().getBytes();
	}
	
	@Rest(type = "search")
	public PageInfo<Image> search(Page model, int start, int limit,
    		String sort, String dir, boolean newSearch, Reference<QLInfo> qlInfoRef) {
		model.setCommonTemplate(true);
		PageInfo<Page> pageInfo = genericService.search(model, start, limit, sort, dir, newSearch, qlInfoRef);
		PageInfo<Image> imagePageInfo = new PageInfo<>();
		List<Image> images = new ArrayList<>();
		for (Page page : pageInfo.getData()) {
			Image image = new Image();
			image.setId(page.getId());
			image.setName(page.getDescription());
			image.setHref("espirit/eap/pagelayout/page._addByPath.html?onlyCenter=true&_layout=whole&path=" + page.getPath());
			image.setSrc(page.getPreviewFileName());
			images.add(image);
		}
		imagePageInfo.setData(images);
		return imagePageInfo;
	}

}
